package org.kosmix.kosmosfs.access;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;

public class KfsInputChannel implements ReadableByteChannel, Positionable {
	private static final int DEFAULT_BUF_SIZE = 1048576;
	private ByteBuffer readBuffer;
	private int kfsFd = -1;
	private long cPtr;

	private static final native int read(long paramLong, int paramInt1,
			ByteBuffer paramByteBuffer, int paramInt2, int paramInt3);

	private static final native int close(long paramLong, int paramInt);

	private static final native int seek(long paramLong1, int paramInt,
			long paramLong2);

	private static final native long tell(long paramLong, int paramInt);

	public KfsInputChannel(long ptr, int fd) {
		this.readBuffer = ByteBuffer.allocateDirect(1048576);
		this.readBuffer.flip();

		this.kfsFd = fd;
		this.cPtr = ptr;
	}

	public boolean isOpen() {
		return this.kfsFd > 0;
	}

	public int read(ByteBuffer dst) throws IOException {
		if (this.kfsFd < 0) {
			throw new IOException("File closed");
		}
		int r0 = dst.remaining();

		while (dst.hasRemaining()) {
			if (!this.readBuffer.hasRemaining()) {
				this.readBuffer.clear();
				readDirect(this.readBuffer);
				this.readBuffer.flip();

				if (!this.readBuffer.hasRemaining()) {
					break;
				}
			}
			int lim = this.readBuffer.limit();

			if (dst.remaining() < this.readBuffer.remaining()) {
				this.readBuffer.limit(this.readBuffer.position()
						+ dst.remaining());
			}

			dst.put(this.readBuffer);

			this.readBuffer.limit(lim);
		}

		int r1 = dst.remaining();
		if ((r1 < r0) || (r0 == 0)) {
			return r0 - r1;
		}
		return -1;
	}

	private void readDirect(ByteBuffer buf) throws IOException {
		if (!buf.isDirect()) {
			throw new IllegalArgumentException("need direct buffer");
		}
		int pos = buf.position();
		int sz = read(this.cPtr, this.kfsFd, buf, pos, buf.limit());
		if (sz < 0) {
			throw new IOException("readDirect failed");
		}

		buf.position(pos + sz);
	}

	public int seek(long offset) throws IOException {
		if (this.kfsFd < 0) {
			throw new IOException("File closed");
		}
		this.readBuffer.clear();
		this.readBuffer.flip();

		return seek(this.cPtr, this.kfsFd, offset);
	}

	public long tell() throws IOException {
		if (this.kfsFd < 0) {
			throw new IOException("File closed");
		}

		return tell(this.cPtr, this.kfsFd) - this.readBuffer.remaining();
	}

	public void close() throws IOException {
		if (this.kfsFd < 0) {
			return;
		}
		close(this.cPtr, this.kfsFd);
		this.kfsFd = -1;
	}

	protected void finalize() throws Throwable {
		if (this.kfsFd < 0)
			return;
		close();
	}
}